/*
 * Copyright (c) 2012 Johann Hanne
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 * THE SOFTWARE.
 */

#include <stdlib.h>
#include <stdint.h>
#include <stdio.h>
#include <string.h>

#include <lua.h>
#include <lauxlib.h>
#include <lualib.h>

#include "bget.h"

static const char account_lua[] =
"-- account.lua\n"
"-- from PiL 1, Chapter 16\n"
"\n"
"Account = {balance = 0}\n"
"\n"
"function Account:new (o, name)\n"
"  o = o or {name=name}\n"
"  setmetatable(o, self)\n"
"  self.__index = self\n"
"  return o\n"
"end\n"
"\n"
"function Account:deposit (v)\n"
"  self.balance = self.balance + v\n"
"end\n"
"\n"
"function Account:withdraw (v)\n"
"  if v > self.balance then error(\"insufficient funds on account \"..self.name) end\n"
"  self.balance = self.balance - v\n"
"end\n"
"\n"
"function Account:show (title)\n"
"  print(title or \"\", self.name, self.balance)\n"
"end\n"
"\n"
"a = Account:new(nil,\"demo\")\n"
"a:show(\"after creation\")\n"
"a:deposit(1000)\n"
"a:show(\"after deposit\")\n"
"a:withdraw(100)\n"
"a:show(\"after withdraw\")\n"
"\n"
"-- this will raise an error\n"
"b = Account:new(nil,\"DEMO\")\n"
"b:withdraw(100)\n"
;

void main(void) {
    char str[10], str2[5];
    lua_State *L;
    int r;
    const char *errmsg;
    bufsize curalloc, totfree, maxfree;
    long nget, nrel;

    /* Note: All other askew610 examples which use malloc and related
       automatically get some heap memory allocated via __heapinit in
       freertos_sq610/heap.c; but as this example doesn't use FreeRTOS,
       we do it manually here */
    bpool((void *)0x2000:>0x0000, 0x10000l);

    printf("Output from C\n");

    L = luaL_newstate();

    luaL_openlibs(L);

    r = luaL_dostring(L, "print(\"I am using Lua from within C\");\n");

    if (r != 0) {
        errmsg = lua_tostring(L, -1);
        printf("Unexpected error occurred:\n--\n%s\n--\n", errmsg);
    }

    r = luaL_dostring(L, account_lua);

    if (r != 0) {
        errmsg = lua_tostring(L, -1);
        printf("Expected error occurred:\n--\n%s\n--\n", errmsg);
    }

    printf("luaL_dostring returned %d\n", r);

    printf("Back to C again\n");

    lua_close(L);

    bstats(&curalloc, &totfree, &maxfree, &nget, &nrel);
    printf("curalloc: %ld, totfree: %ld, maxfree: %ld, nget: %ld, nrel: %ld\n",
           (long int)curalloc, (long int)totfree, (long int)maxfree,
           (long int)nget, (long int)nrel);

    for (;;)
        ;
}

/*
 * We are single-threaded, so we can use trivial bget wrappers here
 *
 * DO NOT USE THESE WRAPPERS FOR APPLICATIONS WITH MULTIPLE THREADS/TASKS
 */

void *calloc(size_t nmemb, size_t size) {
    void *ret = bgetz((bufsize)((bufsize)nmemb * (bufsize)size));
    /*printf("calloc(%zu, %zu) = %p\n", nmemb, size, ret);*/
    return ret;
}

void *malloc(size_t size) {
    void *ret = bget((bufsize)size);
    /*printf("malloc(%zu) = %p\n", size, ret);*/
    return ret;
}

void free(void *ptr) {
    /*printf("free(%p)\n", ptr);*/
    if (ptr != NULL) {
        brel(ptr);
    }
}

void *realloc(void *ptr, size_t size) {
    void *ret;
    if (ptr == NULL) {
        ret = bget((bufsize)size);
    } else {
        if (size == 0) {
            brel(ptr);
            ret = NULL;
        } else {
            ret = bgetr(ptr, (bufsize)size);
        }
    }
    /*printf("realloc(%p, %zu) = %p\n", ptr, size, ret);*/
    return ret;
}
